﻿////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// <copyright>Copyright 2008-2011 Andreas Huber Doenni</copyright>
// Distributed under the GNU General Public License version 2 (GPLv2).
// See accompanying file License.txt or copy at http://phuse.codeplex.com/license.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace Phuse.Net.Nntp
{
    using System;
    using System.Diagnostics.CodeAnalysis;
    using System.Text;

    /// <summary>Represents an AUTHINFO USER and AUTHINFO PASS command pair to be sent to an NNTP server, see
    /// <a href="http://tools.ietf.org/rfc/rfc4643.txt">RFC 4643</a> 2.3.</summary>
    /// <threadsafety static="true" instance="false"/>
    public sealed class AuthInfoCommand : NonPipelinableCommand<AuthInfoResponse>
    {
        private const string AuthenticationAccepted = "281";
        private const string PasswordRequired = "381";
        private static readonly string[] UserWithPassSuccessCodes = { AuthenticationAccepted, PasswordRequired };
        private static readonly string[] AuthenticationAcceptedCodes = { AuthenticationAccepted };

        private readonly string userName;
        private readonly string password;

        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        /// <summary>Initializes a new instance of the <see cref="AuthInfoCommand"/> class.</summary>
        /// <param name="userName">The name of the user to authenticate.</param>
        /// <param name="password">The password to authenticate the user with. The password is only sent if the server
        /// requires it.</param>
        /// <exception cref="ArgumentException"><paramref name="userName"/> and/or <paramref name="password"/> is
        /// <c>null</c> or empty.</exception>
        public AuthInfoCommand(string userName, string password)
        {
            const string Message = "Must not be null or empty.";

            if (string.IsNullOrEmpty(userName))
            {
                throw new ArgumentException(Message, "userName");
            }

            if (string.IsNullOrEmpty(password))
            {
                throw new ArgumentException(Message, "password");
            }

            this.userName = userName;
            this.password = password;
        }

        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        /// <summary>See <see cref="CommandBase{T, U}.SendCommand"/>.</summary>
        protected sealed override void SendCommand(CreateCommandStreamCallback createCommandStreamCallback)
        {
            CommandHelper.WriteSingleLineCommand(createCommandStreamCallback, "AUTHINFO USER " + this.userName);
        }

        /// <summary>See <see cref="NonPipelinableCommandBase{T, U}.ReceiveResponse"/>.</summary>
        [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", Justification = "Not callable from external code, CA bug?")]
        protected sealed override AuthInfoResponse ReceiveResponse(
            CreateResponseStreamCallback createResponseStreamCallback,
            CreateCommandStreamCallback createCommandStreamCallback)
        {
            var response = createResponseStreamCallback(Encoding.UTF8, 2, UserWithPassSuccessCodes);
            response.Dispose();

            if (response.InitialLineWords[0] == PasswordRequired)
            {
                CommandHelper.WriteSingleLineCommand(createCommandStreamCallback, "AUTHINFO PASS " + this.password);
                createResponseStreamCallback(Encoding.UTF8, 2, AuthenticationAcceptedCodes).Dispose();
            }

            return new AuthInfoResponse();
        }
    }
}
